#define DEBUG
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Windows.Forms;
using ICSharpCode.TextEditor.Document;

namespace ICSharpCode.TextEditor
{
    public class TextView : AbstractMargin, IDisposable
    {
        private struct MarkerToDraw
        {
            internal TextMarker marker;

            internal RectangleF drawingRect;

            public MarkerToDraw(TextMarker marker, RectangleF drawingRect)
            {
                this.marker = marker;
                this.drawingRect = drawingRect;
            }
        }

        private struct WordFontPair
        {
            private string word;

            private Font font;

            public WordFontPair(string word, Font font)
            {
                this.word = word;
                this.font = font;
            }

            public override bool Equals(object obj)
            {
                WordFontPair wordFontPair = (WordFontPair)obj;
                if (!word.Equals(wordFontPair.word))
                {
                    return false;
                }
                return font.Equals(wordFontPair.font);
            }

            public override int GetHashCode()
            {
                return word.GetHashCode() ^ font.GetHashCode();
            }
        }

        private int fontHeight;

        private Highlight highlight;

        private int physicalColumn = 0;

        private Font lastFont;

        private int rendingModel = 0;

        private Brush unenableBackgroundBrush = SystemBrushes.InactiveBorder;

        private int spaceWidth;

        private int wideSpaceWidth;

        private int newlineSpaceWidth;

        private int curlyBracketsWidth;

        private const int additionalFoldTextSize = 1;

        private List<MarkerToDraw> markersToDraw = new List<MarkerToDraw>();

        private Dictionary<WordFontPair, int> measureCache = new Dictionary<WordFontPair, int>();

        private const int MaximumWordLength = 1000;

        private const int MaximumCacheSize = 2000;

        private const TextFormatFlags textFormatFlags = TextFormatFlags.NoPrefix | TextFormatFlags.PreserveGraphicsClipping | TextFormatFlags.NoPadding;

        private Dictionary<Font, Dictionary<char, int>> fontBoundCharWidth = new Dictionary<Font, Dictionary<char, int>>();

        private const int MinTabWidth = 4;

        public Highlight Highlight
        {
            get
            {
                return highlight;
            }
            set
            {
                highlight = value;
            }
        }

        public int FirstPhysicalLine => textArea.VirtualTop.Y / fontHeight;

        public int LineHeightRemainder => textArea.VirtualTop.Y % fontHeight;

        public int FirstVisibleLine
        {
            get
            {
                return textArea.Document.GetFirstLogicalLine(textArea.VirtualTop.Y / fontHeight);
            }
            set
            {
                if (FirstVisibleLine != value)
                {
                    textArea.VirtualTop = new Point(textArea.VirtualTop.X, checked(textArea.Document.GetVisibleLine(value) * fontHeight));
                }
            }
        }

        public int VisibleLineDrawingRemainder => textArea.VirtualTop.Y % fontHeight;

        public int FontHeight => fontHeight;

        public int VisibleLineCount
        {
            get
            {
                checked
                {
                    return 1 + unchecked(base.DrawingPosition.Height / fontHeight);
                }
            }
        }

        public int VisibleColumnCount
        {
            get
            {
                checked
                {
                    return unchecked(base.DrawingPosition.Width / WideSpaceWidth) - 1;
                }
            }
        }

        public int RendingModel
        {
            get
            {
                return rendingModel;
            }
            set
            {
                rendingModel = value;
                if (rendingModel == 1)
                {
                    drawingPosition = new Rectangle(0, 0, textArea.Width, textArea.Height);
                }
            }
        }

        public Brush UnenableBackgroundBrush => unenableBackgroundBrush;

        public int SpaceWidth => spaceWidth;

        public int WideSpaceWidth => wideSpaceWidth;

        public int NewlineSpaceWidth => newlineSpaceWidth;

        public int CurlyBracketsWidth => curlyBracketsWidth;

        public void Dispose()
        {
            measureCache.Clear();
        }

        public TextView(TextArea textArea)
            : base(textArea)
        {
            base.Cursor = Cursors.IBeam;
            OptionsChanged();
        }

        private static int GetFontHeight(Font font)
        {
            int height = TextRenderer.MeasureText("_", font).Height;
            checked
            {
                int val = (int)Math.Ceiling(font.GetHeight());
                return Math.Max(height, val) + 1;
            }
        }

        public void OptionsChanged()
        {
            lastFont = base.TextEditorProperties.Font;
            fontHeight = GetFontHeight(lastFont);
            spaceWidth = Math.Max(GetWidth(' ', lastFont), 1);
            wideSpaceWidth = Math.Max(spaceWidth, GetWidth('x', lastFont));
            newlineSpaceWidth = wideSpaceWidth;
            curlyBracketsWidth = Math.Max(GetWidth('{', lastFont), GetWidth('}', lastFont)) / 2;
            string themeName = textArea.Document.ThemeName;
            if (themeName == "Default")
            {
                unenableBackgroundBrush = SystemBrushes.InactiveBorder;
            }
            else if (themeName == "Black")
            {
                unenableBackgroundBrush = new SolidBrush(Color.FromArgb(45, 45, 48));
            }
            if (base.Document.TextEditorProperties.ShowLineNumbers)
            {
            }
            textArea.Document.GuidelinesManager.Update(textArea);
        }

        public override void Paint(Graphics g, Rectangle rect)
        {
            if (rect.Width <= 0 || rect.Height <= 0)
            {
                return;
            }
            if (lastFont != base.TextEditorProperties.Font)
            {
                OptionsChanged();
                textArea.Invalidate();
            }
            int x = textArea.VirtualTop.X;
            if (x > 0)
            {
                g.SetClip(base.DrawingPosition);
            }
            if (!textArea.Enabled)
            {
                g.FillRectangle(unenableBackgroundBrush, base.DrawingPosition);
            }
            textArea.SelectionManager.PaintSelectionRange(g);
            checked
            {
                for (int i = 0; i < unchecked(checked(base.DrawingPosition.Height + VisibleLineDrawingRemainder) / fontHeight) + 1; i++)
                {
                    Rectangle rectangle = new Rectangle(base.DrawingPosition.X - x, base.DrawingPosition.Top + i * fontHeight - VisibleLineDrawingRemainder, base.DrawingPosition.Width + x, fontHeight);
                    if (rect.IntersectsWith(rectangle))
                    {
                        int visibleLine = textArea.Document.GetVisibleLine(FirstVisibleLine);
                        int firstLogicalLine = textArea.Document.GetFirstLogicalLine(textArea.Document.GetVisibleLine(FirstVisibleLine) + i);
                        PaintDocumentLine(g, firstLogicalLine, rectangle);
                    }
                }
                DrawGuidelines(g);
                DrawMarkerDraw(g);
                DrawFoldinglines(g);
                if (x > 0)
                {
                    g.ResetClip();
                }
                textArea.Caret.PaintCaret(g);
            }
        }

        private void PaintDocumentLine(Graphics g, int lineNumber, Rectangle lineRectangle)
        {
            //Debug.Assert(lineNumber >= 0);
            Brush bgColorBrush = GetBgColorBrush(lineNumber);
            Brush brush = (textArea.Enabled ? bgColorBrush : unenableBackgroundBrush);
            HighlightColor colorFor = textArea.Document.HighlightingStrategy.GetColorFor("Selection");
            HighlightColor colorFor2 = textArea.Document.HighlightingStrategy.GetColorFor("MutilSelection");
            int num = lineRectangle.X;
            bool flag = false;
            LineSegment lineSegment = null;
            ColumnRange columnRange = null;
            checked
            {
                if (lineNumber < textArea.Document.TotalNumberOfLines)
                {
                    columnRange = textArea.SelectionManager.GetSelectionAtLine(lineNumber);
                    lineSegment = textArea.Document.GetLineSegment(lineNumber);
                    Brush brush2 = (((columnRange.EndColumn > lineSegment.Length || ColumnRange.WholeColumn.Equals(columnRange)) && base.TextEditorProperties.AllowCaretBeyondEOL) ? bgColorBrush : brush);
                    bool flag2 = FillLineCustomBackgroundColor(g, lineNumber, new RectangleF(num, lineRectangle.Y, lineRectangle.Width - num + lineRectangle.X, lineRectangle.Height));
                    if (base.TextEditorProperties.ShowVerticalRuler)
                    {
                        DrawVerticalRuler(g, lineRectangle);
                    }
                }
                if (lineNumber >= textArea.Document.TotalNumberOfLines)
                {
                    if (base.TextEditorProperties.ShowInvalidLines)
                    {
                        DrawInvalidLineMarker(g, lineRectangle.Left, lineRectangle.Top);
                    }
                    if (base.TextEditorProperties.ShowVerticalRuler)
                    {
                        DrawVerticalRuler(g, lineRectangle);
                    }
                    return;
                }
                int num2 = 0;
                physicalColumn = 0;
                if (base.TextEditorProperties.EnableFolding)
                {
                    while (true)
                    {
                        List<FoldMarker> foldedFoldingsWithStartAfterColumn = textArea.Document.FoldingManager.GetFoldedFoldingsWithStartAfterColumn(lineNumber, num2 - 1);
                        if (foldedFoldingsWithStartAfterColumn == null || foldedFoldingsWithStartAfterColumn.Count <= 0)
                        {
                            if (lineNumber < textArea.Document.TotalNumberOfLines)
                            {
                                num = PaintLinePart(g, lineNumber, colorFor, colorFor2, num2, textArea.Document.GetLineSegment(lineNumber).Length, lineRectangle, num, columnRange, lineSegment);
                            }
                            break;
                        }
                        FoldMarker foldMarker = foldedFoldingsWithStartAfterColumn[0];
                        foreach (FoldMarker item in foldedFoldingsWithStartAfterColumn)
                        {
                            if (item.StartColumn < foldMarker.StartColumn)
                            {
                                foldMarker = item;
                            }
                        }
                        foldedFoldingsWithStartAfterColumn.Clear();
                        num = PaintLinePart(g, lineNumber, colorFor, colorFor2, num2, foldMarker.StartColumn, lineRectangle, num, columnRange, lineSegment);
                        num2 = foldMarker.EndColumn;
                        lineNumber = foldMarker.EndLine;
                        if (lineNumber >= textArea.Document.TotalNumberOfLines)
                        {
                            //Debug.Assert(condition: false, "Folding ends after document end");
                            break;
                        }
                        ColumnRange selectionAtLine = textArea.SelectionManager.GetSelectionAtLine(lineNumber);
                        bool drawSelected = ColumnRange.WholeColumn.Equals(selectionAtLine) || (foldMarker.StartColumn >= selectionAtLine.StartColumn && foldMarker.EndColumn <= selectionAtLine.EndColumn);
                        num = PaintFoldingText(g, lineNumber, num, lineRectangle, foldMarker.FoldText, drawSelected, colorFor);
                    }
                }
                else
                {
                    num = PaintLinePart(g, lineNumber, colorFor, colorFor2, 0, textArea.Document.GetLineSegment(lineNumber).Length, lineRectangle, num, columnRange, lineSegment);
                }
                if (lineNumber >= textArea.Document.TotalNumberOfLines)
                {
                    return;
                }
                if (columnRange == null || lineSegment == null)
                {
                    columnRange = textArea.SelectionManager.GetSelectionAtLine(lineNumber);
                    lineSegment = textArea.Document.GetLineSegment(lineNumber);
                }
                flag = columnRange.EndColumn > lineSegment.Length || ColumnRange.WholeColumn.Equals(columnRange);
                if (base.TextEditorProperties.ShowEOLMarker)
                {
                    HighlightColor colorFor3 = textArea.Document.HighlightingStrategy.GetColorFor("EOLMarkers");
                    num += DrawEOLMarker(g, colorFor3.Color, flag ? bgColorBrush : brush, num, lineRectangle.Y);
                }
                else
                {
                    if (!flag)
                    {
                        return;
                    }
                    if ((lineSegment.Words == null || lineSegment.Words.Count == 0) && !textArea.SelectionManager.IsMutilSelect)
                    {
                        using (SolidBrush brush3 = new SolidBrush(colorFor.BackgroundColor))
                        {
                            g.FillRectangle(brush3, new Rectangle(lineRectangle.X, lineRectangle.Y, newlineSpaceWidth, lineRectangle.Height));
                        }
                    }
                    num += WideSpaceWidth;
                }
            }
        }

        private bool DrawLineMarkerAtLine(int lineNumber)
        {
            return lineNumber == textArea.Caret.Line && textArea.MotherTextAreaControl.TextEditorProperties.LineViewerStyle == LineViewerStyle.FullRow;
        }

        private Brush GetBgColorBrush(int lineNumber)
        {
            if (DrawLineMarkerAtLine(lineNumber) && !textArea.SelectionManager.HasSomethingSelected)
            {
                HighlightColor colorFor = textArea.Document.HighlightingStrategy.GetColorFor("CaretMarker");
                return BrushRegistry.GetBrush(colorFor.Color);
            }
            HighlightColor colorFor2 = textArea.Document.HighlightingStrategy.GetColorFor("Default");
            Color backgroundColor = colorFor2.BackgroundColor;
            return BrushRegistry.GetBrush(backgroundColor);
        }

        private bool FillLineCustomBackgroundColor(Graphics g, int lineNumber, RectangleF lineRectangle)
        {
            if (textArea.TextEditorProperties.ComparisonState && textArea.Document.LineCustomBackgroundColors.ContainsKey(lineNumber))
            {
                Color backgroundColor = textArea.Document.LineCustomBackgroundColors[lineNumber].BackgroundColor;
                Brush brush = BrushRegistry.GetBrush(backgroundColor);
                g.FillRectangle(brush, lineRectangle);
                textArea.SelectionManager.PaintSelectionRange(g, lineNumber, lineRectangle);
                return true;
            }
            return false;
        }

        private int PaintFoldingText(Graphics g, int lineNumber, int physicalXPos, Rectangle lineRectangle, string text, bool drawSelected, HighlightColor selectionColor)
        {
            Brush brush = (drawSelected ? BrushRegistry.GetBrush(selectionColor.BackgroundColor) : GetBgColorBrush(lineNumber));
            Brush brush2 = (textArea.Enabled ? brush : unenableBackgroundBrush);
            Brush brush3 = BrushRegistry.GetBrush(selectionColor.BackgroundColor);
            Font regularFont = textArea.TextEditorProperties.FontContainer.RegularFont;
            int num = checked(MeasureStringWidth(g, text, regularFont) + 2);
            Rectangle rect = new Rectangle(physicalXPos, lineRectangle.Y, num, lineRectangle.Height);
            if (brush2 == brush3 && drawSelected)
            {
                g.FillRectangle(brush2, rect);
            }
            Rectangle rect2 = new Rectangle(physicalXPos, lineRectangle.Y, num, lineRectangle.Height);
            checked
            {
                physicalColumn += text.Length;
                switch (regularFont.FontFamily.Name)
                {
                    case "黑体":
                        DrawString(g, text, regularFont, drawSelected ? selectionColor.Color : Color.Gray, rect2.X + 1, rect2.Y + 1);
                        break;
                    case "宋体":
                    case "仿宋":
                        DrawString(g, text, regularFont, drawSelected ? selectionColor.Color : Color.Gray, rect2.X + 1, rect2.Y + 1);
                        break;
                    case "微软雅黑":
                        DrawString(g, text, regularFont, drawSelected ? selectionColor.Color : Color.Gray, rect2.X + 1, rect2.Y - 1);
                        break;
                    case "Courier New":
                        DrawString(g, text, regularFont, drawSelected ? selectionColor.Color : Color.Gray, rect2.X + 1, rect2.Y);
                        break;
                    default:
                        DrawString(g, text, regularFont, drawSelected ? selectionColor.Color : Color.Gray, rect2);
                        break;
                }
                if (base.Document.TextEditorProperties.FoldMarkStyle < 3)
                {
                    g.DrawRectangle(BrushRegistry.GetPen(drawSelected ? Color.DarkGray : Color.Gray), rect.X, rect.Y, rect.Width, rect.Height - 1);
                }
                return physicalXPos + num + 1;
            }
        }

        private void DrawMarker(Graphics g, TextMarker marker, RectangleF drawingRect)
        {
            markersToDraw.Add(new MarkerToDraw(marker, drawingRect));
        }

        private void DrawMarkerDraw(Graphics g)
        {
            foreach (MarkerToDraw item in markersToDraw)
            {
                TextMarker marker = item.marker;
                RectangleF drawingRect = item.drawingRect;
                float num = drawingRect.Bottom - 1f;
                switch (marker.TextMarkerType)
                {
                    case TextMarkerType.Underlined:
                        g.DrawLine(BrushRegistry.GetPen(marker.Color), drawingRect.X, num, drawingRect.Right, num);
                        break;
                    case TextMarkerType.WaveLine:
                        {
                            int num2 = checked((int)drawingRect.X) % 6;
                            for (float num3 = checked((int)drawingRect.X - num2); num3 < drawingRect.Right; num3 += 6f)
                            {
                                g.DrawLine(BrushRegistry.GetPen(marker.Color), num3, num + 3f - 4f, num3 + 3f, num + 1f - 4f);
                                if (num3 + 3f < drawingRect.Right)
                                {
                                    g.DrawLine(BrushRegistry.GetPen(marker.Color), num3 + 3f, num + 1f - 4f, num3 + 6f, num + 3f - 4f);
                                }
                            }
                            break;
                        }
                    case TextMarkerType.SolidBlock:
                        if (textArea.backgroundImage == null)
                        {
                            g.FillRectangle(BrushRegistry.GetBrush(marker.Color), drawingRect);
                        }
                        break;
                }
            }
            markersToDraw.Clear();
        }

        private void DrawGuidelines(Graphics g)
        {
            base.Document.GuidelinesManager.Render(g, textArea);
        }

        private void DrawFoldinglines(Graphics g)
        {
            if (!base.Document.TextEditorProperties.EnableFolding || base.Document.TextEditorProperties.FoldMarkStyle < 3 || base.Document.FoldingManager.FoldingLines == null)
            {
                return;
            }
            g.SmoothingMode = SmoothingMode.HighQuality;
            HighlightColor colorFor = textArea.Document.HighlightingStrategy.GetColorFor("FoldLine");
            Pen pen = new Pen(colorFor.Color);
            checked
            {
                Rectangle rectangle = new Rectangle(drawingPosition.X - 16, drawingPosition.Y, drawingPosition.Width + 16, drawingPosition.Height);
                for (int i = 0; i < base.Document.FoldingManager.FoldingLines.Count; i++)
                {
                    FoldLine foldLine = base.Document.FoldingManager.FoldingLines[i];
                    if (!foldLine.IsOpened)
                    {
                        g.DrawLine(pen, new Point(rectangle.X, foldLine.Offset), new Point(rectangle.X + rectangle.Width, foldLine.Offset));
                    }
                }
                g.SmoothingMode = SmoothingMode.Default;
            }
        }

        private Brush GetMarkerBrush(IList<TextMarker> markers, ref Color foreColor)
        {
            foreach (TextMarker marker in markers)
            {
                if (marker.TextMarkerType == TextMarkerType.SolidBlock)
                {
                    if (marker.OverrideForeColor)
                    {
                        foreColor = marker.ForeColor;
                    }
                    return BrushRegistry.GetBrush(marker.Color);
                }
            }
            return null;
        }

        private int PaintLinePart(Graphics g, int lineNumber, HighlightColor selectionColor, HighlightColor mutilSelectionColor, int startColumn, int endColumn, Rectangle lineRectangle, int physicalXPos, ColumnRange selectionRange, LineSegment currentLine)
        {
            bool flag = DrawLineMarkerAtLine(lineNumber);
            Brush brush = (textArea.Enabled ? GetBgColorBrush(lineNumber) : unenableBackgroundBrush);
            List<ColumnRange> selectionsAtLine = textArea.SelectionManager.GetSelectionsAtLine(lineNumber);
            HighlightColor colorFor = textArea.Document.HighlightingStrategy.GetColorFor("TabMarkers");
            HighlightColor colorFor2 = textArea.Document.HighlightingStrategy.GetColorFor("SpaceMarkers");
            Brush brush2 = BrushRegistry.GetBrush(selectionColor.BackgroundColor);
            Brush brush3 = BrushRegistry.GetBrush(mutilSelectionColor.BackgroundColor);
            if (currentLine.Words == null)
            {
                return physicalXPos;
            }
            int num = 0;
            TextWord textWord = null;
            FontContainer fontContainer = base.TextEditorProperties.FontContainer;
            checked
            {
                for (int i = 0; i < currentLine.Words.Count; i++)
                {
                    TextWord word = currentLine.Words[i];
                    if (num < startColumn)
                    {
                        num += word.Length;
                        continue;
                    }
                    while (num < endColumn && physicalXPos < lineRectangle.Right)
                    {
                        int num2 = num + word.Length - 1;
                        Color foreColor;
                        switch (word.Type)
                        {
                            case TextWordType.Space:
                                foreColor = colorFor2.Color;
                                break;
                            case TextWordType.Tab:
                                foreColor = colorFor.Color;
                                break;
                            default:
                                foreColor = word.Color;
                                break;
                        }
                        IList<TextMarker> markers = base.Document.MarkerStrategy.GetMarkers(currentLine.Offset + num, word.Length);
                        Brush brush4 = GetMarkerBrush(markers, ref foreColor);
                        foreach (ColumnRange item in selectionsAtLine)
                        {
                            if (item.StartColumn >= num)
                            {
                                selectionRange = item;
                                break;
                            }
                        }
                        if (word.Length > 1)
                        {
                            int num3 = int.MaxValue;
                            if (highlight != null)
                            {
                                if (highlight.OpenBrace.Y == lineNumber && highlight.OpenBrace.X >= num && highlight.OpenBrace.X <= num2)
                                {
                                    num3 = Math.Min(num3, highlight.OpenBrace.X - num);
                                }
                                if (highlight.CloseBrace.Y == lineNumber && highlight.CloseBrace.X >= num && highlight.CloseBrace.X <= num2)
                                {
                                    num3 = Math.Min(num3, highlight.CloseBrace.X - num);
                                }
                                if (num3 == 0)
                                {
                                    num3 = 1;
                                }
                            }
                            if (endColumn < num2)
                            {
                                num3 = Math.Min(num3, endColumn - num);
                            }
                            if (selectionRange.StartColumn > num && selectionRange.StartColumn <= num2)
                            {
                                num3 = Math.Min(num3, selectionRange.StartColumn - num);
                            }
                            else if (selectionRange.EndColumn > num && selectionRange.EndColumn <= num2)
                            {
                                num3 = Math.Min(num3, selectionRange.EndColumn - num);
                            }
                            foreach (TextMarker item2 in markers)
                            {
                                int num4 = item2.Offset - currentLine.Offset;
                                int num5 = item2.EndOffset - currentLine.Offset + 1;
                                if (num4 > num && num4 <= num2)
                                {
                                    num3 = Math.Min(num3, num4 - num);
                                }
                                else if (num5 > num && num5 <= num2)
                                {
                                    num3 = Math.Min(num3, num5 - num);
                                }
                            }
                            if (num3 != int.MaxValue)
                            {
                                if (textWord != null)
                                {
                                    throw new ApplicationException("split part invalid: first part cannot be splitted further");
                                }
                                textWord = TextWord.Split(ref word, num3);
                                continue;
                            }
                        }
                        if (ColumnRange.WholeColumn.Equals(selectionRange) || (selectionRange.StartColumn <= num && selectionRange.EndColumn > num2))
                        {
                            if (selectionRange.IsCurrent)
                            {
                                brush4 = brush2;
                                if (selectionColor.HasForeground)
                                {
                                    foreColor = selectionColor.Color;
                                }
                            }
                            else
                            {
                                brush4 = brush3;
                                if (mutilSelectionColor.HasForeground)
                                {
                                    foreColor = mutilSelectionColor.Color;
                                }
                            }
                        }
                        else if (flag)
                        {
                            brush4 = brush;
                        }
                        if (brush4 == null)
                        {
                            brush4 = ((word.SyntaxColor == null || !word.SyntaxColor.HasBackground) ? brush : BrushRegistry.GetBrush(word.SyntaxColor.BackgroundColor));
                        }
                        RectangleF rectangleF;
                        if (word.Type == TextWordType.Space)
                        {
                            physicalColumn++;
                            rectangleF = new RectangleF(physicalXPos, lineRectangle.Y, SpaceWidth, lineRectangle.Height);
                            if (brush4 == brush2)
                            {
                                g.FillRectangle(brush4, rectangleF);
                            }
                            if (base.TextEditorProperties.ShowSpaces)
                            {
                                DrawSpaceMarker(g, foreColor, physicalXPos, lineRectangle.Y);
                            }
                            physicalXPos += SpaceWidth;
                        }
                        else if (word.Type == TextWordType.Tab)
                        {
                            physicalColumn += base.TextEditorProperties.TabIndent;
                            physicalColumn = unchecked(physicalColumn / base.TextEditorProperties.TabIndent) * base.TextEditorProperties.TabIndent;
                            int num6 = unchecked(checked(physicalXPos + 4 - lineRectangle.X) / WideSpaceWidth / base.TextEditorProperties.TabIndent) * WideSpaceWidth * base.TextEditorProperties.TabIndent + lineRectangle.X;
                            num6 += WideSpaceWidth * base.TextEditorProperties.TabIndent;
                            rectangleF = new RectangleF(physicalXPos, lineRectangle.Y, num6 - physicalXPos, lineRectangle.Height);
                            if (brush4 == brush2)
                            {
                                g.FillRectangle(brush4, rectangleF);
                            }
                            if (base.TextEditorProperties.ShowTabs)
                            {
                                DrawTabMarker(g, foreColor, physicalXPos, lineRectangle.Y);
                            }
                            physicalXPos = num6;
                        }
                        else
                        {
                            int num7 = DrawDocumentWord(g, currentLine, word.Word, new Point(physicalXPos, lineRectangle.Y), word.GetFont(fontContainer), foreColor, brush4, brush2, brush3);
                            rectangleF = new RectangleF(physicalXPos, lineRectangle.Y, num7, lineRectangle.Height);
                            physicalXPos += num7;
                        }
                        foreach (TextMarker item3 in markers)
                        {
                            if (item3.TextMarkerType != TextMarkerType.SolidBlock)
                            {
                                DrawMarker(g, item3, rectangleF);
                            }
                        }
                        if (highlight != null && ((highlight.OpenBrace.Y == lineNumber && highlight.OpenBrace.X == num) || (highlight.CloseBrace.Y == lineNumber && highlight.CloseBrace.X == num)))
                        {
                            DrawBracketHighlight(g, new Rectangle((int)rectangleF.X, lineRectangle.Y, (int)rectangleF.Width - 1, lineRectangle.Height - 1));
                        }
                        num += word.Length;
                        if (textWord != null)
                        {
                            word = textWord;
                            textWord = null;
                            continue;
                        }
                        goto IL_0943;
                    }
                    break;
                IL_0943:;
                }
                if (physicalXPos < lineRectangle.Right && endColumn >= currentLine.Length)
                {
                    IList<TextMarker> markers2 = base.Document.MarkerStrategy.GetMarkers(currentLine.Offset + currentLine.Length);
                    foreach (TextMarker item4 in markers2)
                    {
                        if (item4.TextMarkerType != TextMarkerType.SolidBlock)
                        {
                            DrawMarker(g, item4, new RectangleF(physicalXPos, lineRectangle.Y, WideSpaceWidth, lineRectangle.Height));
                        }
                    }
                }
                return physicalXPos;
            }
        }

        private int DrawDocumentWord(Graphics g, LineSegment line, string word, Point position, Font font, Color foreColor, Brush backBrush, Brush selectionBackgroundBrush, Brush mutilSelectionBackgroundBrush)
        {
            if (word == null || word.Length == 0)
            {
                return 0;
            }
            checked
            {
                if (word.Length > 1000)
                {
                    int num = 0;
                    for (int i = 0; i < word.Length; i += 1000)
                    {
                        Point position2 = position;
                        position2.X += num;
                        num = ((i + 1000 >= word.Length) ? (num + DrawDocumentWord(g, line, word.Substring(i, word.Length - i), position2, font, foreColor, backBrush, selectionBackgroundBrush, mutilSelectionBackgroundBrush)) : (num + DrawDocumentWord(g, line, word.Substring(i, 1000), position2, font, foreColor, backBrush, selectionBackgroundBrush, mutilSelectionBackgroundBrush)));
                    }
                    return num;
                }
                int num2 = MeasureStringWidth(g, word, font);
                if (backBrush == selectionBackgroundBrush || backBrush == mutilSelectionBackgroundBrush || (!DrawLineMarkerAtLine(line.LineNumber) && backBrush != BrushRegistry.GetBrush(textArea.BackColor)))
                {
                    g.FillRectangle(backBrush, new RectangleF(position.X, position.Y, num2 + 1, FontHeight));
                }
                DrawString(g, word, font, foreColor, position.X, position.Y);
                return num2;
            }
        }

        private int MeasureStringWidth(Graphics g, string word, Font font)
        {
            if (word == null || word.Length == 0)
            {
                return 0;
            }
            checked
            {
                int num;
                if (word.Length > 1000)
                {
                    num = 0;
                    for (int i = 0; i < word.Length; i += 1000)
                    {
                        num = ((i + 1000 >= word.Length) ? (num + MeasureStringWidth(g, word.Substring(i, word.Length - i), font)) : (num + MeasureStringWidth(g, word.Substring(i, 1000), font)));
                    }
                    return num;
                }
                if (measureCache.TryGetValue(new WordFontPair(word, font), out num))
                {
                    return num;
                }
                if (measureCache.Count > 2000)
                {
                    measureCache.Clear();
                }
                num = TextRenderer.MeasureText(g, word, font, new Size(32767, 32767), TextFormatFlags.NoPrefix | TextFormatFlags.PreserveGraphicsClipping | TextFormatFlags.NoPadding).Width;
                measureCache.Add(new WordFontPair(word, font), num);
                return num;
            }
        }

        public int GetWidth(char ch, Font font)
        {
            if (!fontBoundCharWidth.ContainsKey(font))
            {
                fontBoundCharWidth.Add(font, new Dictionary<char, int>());
            }
            if (!fontBoundCharWidth[font].ContainsKey(ch))
            {
                using (Graphics g = textArea.CreateGraphics())
                {
                    return GetWidth(g, ch, font);
                }
            }
            return fontBoundCharWidth[font][ch];
        }

        public int GetWidth(Graphics g, char ch, Font font)
        {
            if (!fontBoundCharWidth.ContainsKey(font))
            {
                fontBoundCharWidth.Add(font, new Dictionary<char, int>());
            }
            if (!fontBoundCharWidth[font].ContainsKey(ch))
            {
                fontBoundCharWidth[font].Add(ch, MeasureStringWidth(g, ch.ToString(), font));
            }
            return fontBoundCharWidth[font][ch];
        }

        public int GetVisualColumn(int logicalLine, int logicalColumn)
        {
            int column = 0;
            using (Graphics g = textArea.CreateGraphics())
            {
                CountColumns(ref column, 0, logicalColumn, logicalLine, g);
            }
            return column;
        }

        public int GetVisualColumnFast(LineSegment line, int logicalColumn)
        {
            int offset = line.Offset;
            int tabIndent = base.Document.TextEditorProperties.TabIndent;
            int num = 0;
            checked
            {
                for (int i = 0; i < logicalColumn; i++)
                {
                    char c = ((i < line.Length) ? base.Document.GetCharAt(offset + i) : ' ');
                    char c2 = c;
                    if (c2 != '\t')
                    {
                        num = ((c < '一' || c > '龻') ? (1 + num) : (2 + num));
                        continue;
                    }
                    num += tabIndent;
                    num = unchecked(num / tabIndent) * tabIndent;
                }
                return num;
            }
        }

        public TextLocation GetLogicalPosition(Point mousePosition)
        {
            FoldMarker inFoldMarker;
            return GetLogicalColumn(GetLogicalLine(mousePosition.Y), mousePosition.X, out inFoldMarker);
        }

        public TextLocation GetLogicalPosition(int visualPosX, int visualPosY)
        {
            FoldMarker inFoldMarker;
            return GetLogicalColumn(GetLogicalLine(visualPosY), visualPosX, out inFoldMarker);
        }

        public FoldMarker GetFoldMarkerFromPosition(int visualPosX, int visualPosY)
        {
            GetLogicalColumn(GetLogicalLine(visualPosY), visualPosX, out var inFoldMarker);
            return inFoldMarker;
        }

        public int GetLogicalLine(int visualPosY)
        {
            int lineNumber = Math.Max(0, checked(visualPosY + textArea.VirtualTop.Y) / fontHeight);
            return base.Document.GetFirstLogicalLine(lineNumber);
        }

        internal TextLocation GetLogicalColumn(int lineNumber, int visualPosX, out FoldMarker inFoldMarker)
        {
            visualPosX = checked(visualPosX + textArea.VirtualTop.X);
            inFoldMarker = null;
            if (lineNumber >= base.Document.TotalNumberOfLines)
            {
                return new TextLocation(visualPosX / WideSpaceWidth, lineNumber);
            }
            if (visualPosX <= 0)
            {
                return new TextLocation(0, lineNumber);
            }
            int num = 0;
            int drawingPos = 0;
            checked
            {
                int logicalColumnInternal;
                using (Graphics g = textArea.CreateGraphics())
                {
                    while (true)
                    {
                        LineSegment lineSegment = base.Document.GetLineSegment(lineNumber);
                        FoldMarker foldMarker = FindNextFoldedFoldingOnLineAfterColumn(lineNumber, num - 1);
                        int num2 = foldMarker?.StartColumn ?? int.MaxValue;
                        logicalColumnInternal = GetLogicalColumnInternal(g, lineSegment, num, num2, ref drawingPos, visualPosX);
                        if (logicalColumnInternal < num2)
                        {
                            break;
                        }
                        lineNumber = foldMarker.EndLine;
                        num = foldMarker.EndColumn;
                        int num3 = drawingPos + 1 + MeasureStringWidth(g, foldMarker.FoldText, base.TextEditorProperties.FontContainer.RegularFont);
                        if (num3 >= visualPosX)
                        {
                            inFoldMarker = foldMarker;
                            if (IsNearerToAThanB(visualPosX, drawingPos, num3))
                            {
                                return new TextLocation(foldMarker.StartColumn, foldMarker.StartLine);
                            }
                            return new TextLocation(foldMarker.EndColumn, foldMarker.EndLine);
                        }
                        drawingPos = num3;
                    }
                }
                return new TextLocation(logicalColumnInternal, lineNumber);
            }
        }

        private int GetLogicalColumnInternal(Graphics g, LineSegment line, int start, int end, ref int drawingPos, int targetVisualPosX)
        {
            if (start == end)
            {
                return end;
            }
            //Debug.Assert(start < end);
            //Debug.Assert(drawingPos < targetVisualPosX);
            int tabIndent = base.Document.TextEditorProperties.TabIndent;
            FontContainer fontContainer = base.TextEditorProperties.FontContainer;
            List<TextWord> words = line.Words;
            if (words == null)
            {
                return 0;
            }
            int num = 0;
            checked
            {
                for (int i = 0; i < words.Count; i++)
                {
                    TextWord textWord = words[i];
                    if (num >= end)
                    {
                        return num;
                    }
                    if (num + textWord.Length >= start)
                    {
                        int num3;
                        switch (textWord.Type)
                        {
                            case TextWordType.Space:
                                num3 = drawingPos + spaceWidth;
                                if (num3 >= targetVisualPosX)
                                {
                                    return IsNearerToAThanB(targetVisualPosX, drawingPos, num3) ? num : (num + 1);
                                }
                                break;
                            case TextWordType.Tab:
                                drawingPos = unchecked(checked(drawingPos + 4) / tabIndent / WideSpaceWidth) * tabIndent * WideSpaceWidth;
                                num3 = drawingPos + tabIndent * WideSpaceWidth;
                                if (num3 >= targetVisualPosX)
                                {
                                    return IsNearerToAThanB(targetVisualPosX, drawingPos, num3) ? num : (num + 1);
                                }
                                break;
                            case TextWordType.Word:
                                {
                                    int num2 = Math.Max(num, start);
                                    int length = Math.Min(num + textWord.Length, end) - num2;
                                    string text = base.Document.GetText(line.Offset + num2, length);
                                    Font font = textWord.GetFont(fontContainer) ?? fontContainer.RegularFont;
                                    num3 = drawingPos + MeasureStringWidth(g, text, font);
                                    if (num3 < targetVisualPosX)
                                    {
                                        break;
                                    }
                                    for (int j = 0; j < text.Length; j++)
                                    {
                                        num3 = drawingPos + MeasureStringWidth(g, text[j].ToString(), font);
                                        if (num3 >= targetVisualPosX)
                                        {
                                            if (IsNearerToAThanB(targetVisualPosX, drawingPos, num3))
                                            {
                                                return num2 + j;
                                            }
                                            return num2 + j + 1;
                                        }
                                        drawingPos = num3;
                                    }
                                    return num2 + text.Length;
                                }
                            default:
                                throw new NotSupportedException();
                        }
                        drawingPos = num3;
                    }
                    num += textWord.Length;
                }
                return num;
            }
        }

        private static bool IsNearerToAThanB(int num, int a, int b)
        {
            return checked(Math.Abs(a - num) < Math.Abs(b - num));
        }

        private FoldMarker FindNextFoldedFoldingOnLineAfterColumn(int lineNumber, int column)
        {
            List<FoldMarker> foldedFoldingsWithStartAfterColumn = base.Document.FoldingManager.GetFoldedFoldingsWithStartAfterColumn(lineNumber, column);
            if (foldedFoldingsWithStartAfterColumn.Count != 0)
            {
                return foldedFoldingsWithStartAfterColumn[0];
            }
            return null;
        }

        private float CountColumns(ref int column, int start, int end, int logicalLine, Graphics g)
        {
            if (start > end)
            {
                throw new ArgumentException("start > end");
            }
            if (start == end)
            {
                return 0f;
            }
            float num = SpaceWidth;
            float num2 = 0f;
            int tabIndent = base.Document.TextEditorProperties.TabIndent;
            LineSegment lineSegment = base.Document.GetLineSegment(logicalLine);
            List<TextWord> words = lineSegment.Words;
            if (words == null)
            {
                return 0f;
            }
            int count = words.Count;
            int num3 = 0;
            FontContainer fontContainer = base.TextEditorProperties.FontContainer;
            checked
            {
                for (int i = 0; i < count; i++)
                {
                    TextWord textWord = words[i];
                    if (num3 >= end)
                    {
                        break;
                    }
                    if (num3 + textWord.Length >= start)
                    {
                        switch (textWord.Type)
                        {
                            case TextWordType.Space:
                                num2 += num;
                                break;
                            case TextWordType.Tab:
                                num2 = (int)((num2 + 4f) / (float)tabIndent / (float)WideSpaceWidth) * tabIndent * WideSpaceWidth;
                                num2 += (float)(tabIndent * WideSpaceWidth);
                                break;
                            case TextWordType.Word:
                                {
                                    int num4 = Math.Max(num3, start);
                                    int length = Math.Min(num3 + textWord.Length, end) - num4;
                                    string text = base.Document.GetText(lineSegment.Offset + num4, length);
                                    num2 += (float)MeasureStringWidth(g, text, textWord.GetFont(fontContainer) ?? fontContainer.RegularFont);
                                    break;
                                }
                        }
                    }
                    num3 += textWord.Length;
                }
                for (int j = lineSegment.Length; j < end; j++)
                {
                    num2 += (float)WideSpaceWidth;
                }
                column += (int)((num2 + 1f) / (float)WideSpaceWidth);
                return num2;
            }
        }

        public int GetDrawingXPos(int logicalLine, int logicalColumn)
        {
            List<FoldMarker> topLevelFoldedFoldings = base.Document.FoldingManager.GetTopLevelFoldedFoldings();
            FoldMarker foldMarker = null;
            checked
            {
                int num;
                for (num = topLevelFoldedFoldings.Count - 1; num >= 0; num--)
                {
                    foldMarker = topLevelFoldedFoldings[num];
                    if (foldMarker.StartLine < logicalLine || (foldMarker.StartLine == logicalLine && foldMarker.StartColumn < logicalColumn))
                    {
                        break;
                    }
                    unchecked
                    {
                        FoldMarker foldMarker2 = topLevelFoldedFoldings[num / 2];
                        if (foldMarker2.StartLine > logicalLine || (foldMarker2.StartLine == logicalLine && foldMarker2.StartColumn >= logicalColumn))
                        {
                            num /= 2;
                        }
                    }
                }
                int num2 = 0;
                int num3 = 0;
                int column = 0;
                int tabIndent = base.Document.TextEditorProperties.TabIndent;
                Graphics graphics = textArea.CreateGraphics();
                float num4;
                if (foldMarker == null || (foldMarker.StartLine >= logicalLine && (foldMarker.StartLine != logicalLine || foldMarker.StartColumn >= logicalColumn)))
                {
                    num4 = CountColumns(ref column, 0, logicalColumn, logicalLine, graphics);
                    return (int)(num4 - (float)textArea.VirtualTop.X);
                }
                if (foldMarker.EndLine > logicalLine || (foldMarker.EndLine == logicalLine && foldMarker.EndColumn > logicalColumn))
                {
                    logicalColumn = foldMarker.StartColumn;
                    logicalLine = foldMarker.StartLine;
                    num--;
                }
                num2 = num;
                while (num >= 0)
                {
                    foldMarker = topLevelFoldedFoldings[num];
                    if (foldMarker.EndLine < logicalLine)
                    {
                        break;
                    }
                    num--;
                }
                num3 = num + 1;
                if (num2 < num3)
                {
                    num4 = CountColumns(ref column, 0, logicalColumn, logicalLine, graphics);
                    return (int)(num4 - (float)textArea.VirtualTop.X);
                }
                int start = 0;
                num4 = 0f;
                for (num = num3; num <= num2; num++)
                {
                    foldMarker = topLevelFoldedFoldings[num];
                    num4 += CountColumns(ref column, start, foldMarker.StartColumn, foldMarker.StartLine, graphics);
                    start = foldMarker.EndColumn;
                    column += foldMarker.FoldText.Length;
                    num4 += 1f;
                    num4 += (float)MeasureStringWidth(graphics, foldMarker.FoldText, base.TextEditorProperties.FontContainer.RegularFont);
                }
                num4 += CountColumns(ref column, start, logicalColumn, logicalLine, graphics);
                graphics.Dispose();
                return (int)(num4 - (float)textArea.VirtualTop.X);
            }
        }

        private void DrawBracketHighlight(Graphics g, Rectangle rect)
        {
            HighlightColor colorFor = textArea.Document.HighlightingStrategy.GetColorFor("Bracket");
            g.FillRectangle(BrushRegistry.GetBrush(colorFor.BackgroundColor), rect);
            g.DrawRectangle(new Pen(colorFor.Color), rect);
        }

        private void DrawString(Graphics g, string text, Font font, Color color, int x, int y)
        {
            TextRenderer.DrawText(g, text, font, new Point(x, y), color, TextFormatFlags.NoPrefix | TextFormatFlags.PreserveGraphicsClipping | TextFormatFlags.NoPadding);
        }

        private void DrawString(Graphics g, string text, Font font, Color color, Rectangle rect)
        {
            TextRenderer.DrawText(g, text, font, rect, color, TextFormatFlags.HorizontalCenter | TextFormatFlags.VerticalCenter);
        }

        private void DrawInvalidLineMarker(Graphics g, int x, int y)
        {
            HighlightColor colorFor = textArea.Document.HighlightingStrategy.GetColorFor("InvalidLines");
            DrawString(g, "~", colorFor.GetFont(base.TextEditorProperties.FontContainer), colorFor.Color, x, y);
        }

        private void DrawSpaceMarker(Graphics g, Color color, int x, int y)
        {
            HighlightColor colorFor = textArea.Document.HighlightingStrategy.GetColorFor("SpaceMarkers");
            DrawString(g, "·", colorFor.GetFont(base.TextEditorProperties.FontContainer), color, x, y);
        }

        private void DrawTabMarker(Graphics g, Color color, int x, int y)
        {
            HighlightColor colorFor = textArea.Document.HighlightingStrategy.GetColorFor("TabMarkers");
            DrawString(g, "»", colorFor.GetFont(base.TextEditorProperties.FontContainer), color, x, y);
        }

        private int DrawEOLMarker(Graphics g, Color color, Brush backBrush, int x, int y)
        {
            HighlightColor colorFor = textArea.Document.HighlightingStrategy.GetColorFor("EOLMarkers");
            HighlightColor colorFor2 = textArea.Document.HighlightingStrategy.GetColorFor("Selection");
            Brush brush = BrushRegistry.GetBrush(colorFor2.BackgroundColor);
            int width = GetWidth('¶', colorFor.GetFont(base.TextEditorProperties.FontContainer));
            if (textArea.backgroundImage == null || backBrush == brush)
            {
                g.FillRectangle(backBrush, new RectangleF(x, y, width, fontHeight));
            }
            DrawString(g, "¶", colorFor.GetFont(base.TextEditorProperties.FontContainer), color, x, y);
            return width;
        }

        private void DrawVerticalRuler(Graphics g, Rectangle lineRectangle)
        {
            checked
            {
                int num = WideSpaceWidth * base.TextEditorProperties.VerticalRulerRow - textArea.VirtualTop.X;
                if (num > 0)
                {
                    int num2 = drawingPosition.Left + 1;
                    HighlightColor colorFor = textArea.Document.HighlightingStrategy.GetColorFor("VRuler");
                    g.DrawLine(BrushRegistry.GetPen(colorFor.Color), num2 + num, lineRectangle.Top, num2 + num, lineRectangle.Bottom);
                }
            }
        }
    }
}
